/*
 *  HTTP/TCP and FTP traffic simulation model from permanent
 *  document IEEE 802.16.3c-01/30r1 of the IEEE 802.16 committee
 *
 *  C.Cicconetti, University of Pisa, 2006.
 */

#include <bwa_internet.h>

//! Transition probabilities for the ON cases.
const double transition_on[] =  { 4.571e-1, 1.445e-2, 4.571e-4, 4.571e-6 };

//! Transition probabilities for the OFF cases.
const double transition_off[] = { 3.429e-1, 1.084e-2, 3.429e-4, 3.429e-6 };

//! Packet rate for the ON cases.
const double packet_rate[] = { 2.679, 1.698, 1.388, 1.234 };

static class BwaInternetClass : public TclClass {
 public:
	BwaInternetClass() : TclClass("Application/Traffic/BwaInternet") {}
	TclObject* create(int, const char*const*) {
		return (new BwaInternet());
	}
} class_bwa_internet;


BwaInternet::BwaInternet()
{
	rate_ = 100000;
	packetSize_ = 192;
	trace_ = 0;
	init ();
}

void
BwaInternet::init()
{
	// average rate of packets per time-unit
	double x = ( rate_ / ( 8.0 * packetSize_ ) ) / 3.0;

	// set the size to the constant packet size
	size_ = packetSize_;

	// initialize the exponential random variables
	// and set all processes as zero burst
	for ( unsigned int i = 0 ; i < N_IPP ; i++ ) {
		ipp_on_[i].setavg ( 1.0 / ( x * transition_on[i] ) );
		ipp_off_[i].setavg ( 1.0 / ( x * transition_off[i] ) );
		packetRate_[i] = packet_rate[i] * x;
		burstSize_[i] = 0;
	}

	// set the current burst to -1
	current_ = -1;

	// if (agent_) agent_->set_pkttype(PT_EXP);
}

void
BwaInternet::start()
{
	init();
	running_ = 1;
	timeout();
}

double BwaInternet::next_interval(int& size)
{
	// size_ never changes
	size = size_;

	// if this is the first call after init(), then we compute the start
	// time for all the processes
	if ( current_ < 0 ) {
		for ( unsigned int i = 0 ; i < N_IPP ; i++ ) {
			burstSize_[i] = int (.5 + ipp_on_[i].value() * packetRate_[i]);
			if ( burstSize_[i] < 1 ) burstSize_[i] = 1;
			nextInterval_[i] = NOW + 1 / packetRate_[i];
		}
		current_ = smallest ( nextInterval_ );
		return nextInterval_[current_] - NOW;
	}

	// if the current burst has size zero, then start a new burst
	if ( burstSize_[current_] == 0 ) {
		burstSize_[current_] =
			int (.5 + ipp_on_[current_].value() * packetRate_[current_]);
		if ( burstSize_[current_] < 1 ) burstSize_[current_] = 1;
	}

	burstSize_[current_]--;

	// if the current burst is done, backoff
	if ( burstSize_[current_] == 0 ) {
		nextInterval_[current_] = NOW + ipp_off_[current_].value();

	} else {
		nextInterval_[current_] = NOW + 1 / packetRate_[current_];
	}

	// compute the next interval
	current_ = smallest (nextInterval_);

	// print out trace samples, if needed
	if ( trace_ ) fprintf (trace_, "%f %d\n",
			1000.0 * ( nextInterval_[current_] - NOW ), size_);
		
	return ( nextInterval_[current_] - NOW > 0 )
		? nextInterval_[current_] - NOW  : 0;
}

void
BwaInternet::timeout()
{
	if (! running_)
		return;

	/* send a packet */

	//if (nextPkttime_ != interval_ || nextPkttime_ == -1) 
	if (nextPkttime_ == -1) 
		agent_->sendmsg(size_, "NEW_BURST");
	else 
		agent_->sendmsg(size_);

	/* figure out when to send the next one */
	nextPkttime_ = next_interval(size_);

	/* schedule it */
	if (nextPkttime_ >= 0)
		timer_.resched(nextPkttime_);
}

int
BwaInternet::command (int argc, const char*const* argv)
{
	if ( argc == 3 && strcmp (argv[1], "use-rng") == 0 ) {
		for ( unsigned int i = 0 ; i < N_IPP ; i++ ) {
			ipp_on_[i].seed ( (char*) argv[2] );
			ipp_off_[i].seed ( (char*) argv[2] );
		}
		return TCL_OK;

	} else if ( argc == 3 && strcmp (argv[1], "trace-out") == 0 ) {
		trace_ = fopen (argv[2], "w");
		if ( ! trace_ ) {
			fprintf (stderr, "Could not open '%s'\n", argv[2]);
			return TCL_ERROR;
		}
		return TCL_OK;

	} else if ( argc == 3 && strcmp (argv[1], "rate") == 0 ) {
		if ( atof (argv[2]) <= 0 ) {
			fprintf (stderr, "Invalid rate '%s'. "
					"Choose a positive number of bit/s\n", argv[2]);
			return TCL_ERROR;
		}
		rate_ = atof (argv[2]);
		return TCL_OK;

	} else if ( argc == 3 && strcmp (argv[1], "pkt-size") == 0 ) {
		if ( atoi (argv[2]) <= 0 ) {
			fprintf (stderr, "Invalid packet size '%s'. "
					"Choose a positive number of bytes\n", argv[2]);
			return TCL_ERROR;
		}
		rate_ = (unsigned int) atoi (argv[2]);
		return TCL_OK;
	}

	return Application::command(argc,argv);
}

unsigned int
BwaInternet::smallest (double x[N_IPP])
{
	double low = x[0];
	unsigned int cur = 0;
	for ( unsigned int i = 1 ; i < N_IPP ; i++ ) {
		if ( x[i] < low ) {
			cur = i;
			low = x[i];
		}
	}
	return cur;
}
